package com.google.code.p.restrpc.directory;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.p.restrpc.ServiceNode;

/**
 * @author dz
 */
public class ServiceDefinitionGroup {
    
    private static final Log logger = LogFactory.getLog(ServiceDefinitionGroup.class);
    
    private String serviceName;
    
    private ConcurrentHashMap<String, RemoteServiceDefinition> serviceDefinitions;
    
    private ConcurrentHashMap<String, ServiceNode> serviceNodes;
    
    private long lastModifiedMillis = -1l;
    
    /**
     * @param serviceName
     */
    public ServiceDefinitionGroup(String serviceName) {
        this.serviceName = serviceName;
        this.serviceDefinitions = new ConcurrentHashMap<String, RemoteServiceDefinition>();
        this.serviceNodes = new ConcurrentHashMap<String, ServiceNode>();
    }
    
    public String getServiceName() {
        return this.serviceName;
    }
    
    /**
     * @param rsd
     * @param sn
     */
    public void add(RemoteServiceDefinition rsd, ServiceNode sn) {
        serviceDefinitions.putIfAbsent(rsd.toServiceUrl(), rsd);
        serviceNodes.putIfAbsent(rsd.toServiceUrl(), sn);
        // change the lastModifiedMillis which will use to notifythe client
        this.setLastModifiedMillis(System.currentTimeMillis());
    }
    
    /**
     * @param rsd
     */
    public void disable(RemoteServiceDefinition rsd) {
        RemoteServiceDefinition remoteServiceDefinition = serviceDefinitions.get(rsd.toServiceUrl());
        if (remoteServiceDefinition == null) {
            return;
        }
        if (!remoteServiceDefinition.isEnabled()) {
            return;
        }
        remoteServiceDefinition.disable();
        // change the lastModifiedMillis which will use to notifythe client
        this.setLastModifiedMillis(System.currentTimeMillis());
    }
    
    /**
     * @param rsd
     */
    public void enable(RemoteServiceDefinition rsd) {
        RemoteServiceDefinition remoteServiceDefinition = serviceDefinitions.get(rsd.toServiceUrl());
        if (remoteServiceDefinition == null) {
            return;
        }
        if (remoteServiceDefinition.isEnabled()) {
            return;
        }
        remoteServiceDefinition.enable();
        // change the lastModifiedMillis which will use to notify the client
        this.setLastModifiedMillis(System.currentTimeMillis());
    }
    
    /**
     * @param rsd
     */
    public void remove(RemoteServiceDefinition rsd) {
        serviceDefinitions.remove(rsd.toServiceUrl());
        serviceNodes.remove(rsd.toServiceUrl());
        // change the lastModifiedMillis which will use to notify the client
        this.setLastModifiedMillis(System.currentTimeMillis());
    }
    
    /**
     * @return
     */
    public RemoteServiceDefinition[] toServiceDefinitionArray() {
        return (RemoteServiceDefinition[]) serviceDefinitions.values()
                                                             .toArray(new RemoteServiceDefinition[] {});
        // List<ServiceDefinition> result = new ArrayList<ServiceDefinition>();
        // for (Iterator<Entry<String, ServiceNode>> iterator =
        // serviceNodes.entrySet()
        // .iterator(); iterator.hasNext();) {
        // Entry<String, ServiceNode> entry = iterator.next();
        // ServiceDefinition item = new ServiceDefinition();
        // item.setServiceName(this.serviceName);
        // item.setRemoteAddress(entry.getValue().getAddress());
        // item.setRemotePort(entry.getValue().getPort());
        // item.setContextPath(entry.getValue().getContextPath());
        // result.add(item);
        // }
        // return result.toArray(new ServiceDefinition[] {});
    }
    
    /**
     * @param address
     * @param port
     */
    public void remove(String address, int port) {
        boolean modified = false;
        //
        for (Iterator<Entry<String, ServiceNode>> iterator = serviceNodes.entrySet()
                                                                         .iterator(); iterator.hasNext();) {
            Entry<String, ServiceNode> entry = iterator.next();
            if (entry.getValue().getAddress().equals(address) && entry.getValue()
                                                                      .getPort() == port) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Unregistered Node:" + entry.getValue());
                }
                iterator.remove();
                modified = true;
            }
        }
        
        //
        for (Iterator<Entry<String, RemoteServiceDefinition>> iterator = serviceDefinitions.entrySet()
                                                                                           .iterator(); iterator.hasNext();) {
            Entry<String, RemoteServiceDefinition> entry = iterator.next();
            if (entry.getValue().getRemoteAddress().equals(address) && entry.getValue()
                                                                            .getRemotePort() == port) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Unregistered Service:" + entry.getKey());
                }
                iterator.remove();
                modified = true;
            }
        }
        //
        if (modified) {
            // change the lastModifiedMillis which will use to notify the client
            this.setLastModifiedMillis(System.currentTimeMillis());
        }
    }
    
    /**
     * @return the lastModifiedMillis
     */
    public long getLastModifiedMillis() {
        return lastModifiedMillis;
    }
    
    /**
     * @param lastModifiedMillis
     *            the lastModifiedMillis to set
     */
    private void setLastModifiedMillis(long lastModifiedMillis) {
        this.lastModifiedMillis = lastModifiedMillis;
    }
    
    /**
     * @return
     */
    public boolean isEmpty() {
        return (serviceDefinitions.isEmpty() && serviceNodes.isEmpty());
    }
}
